// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt
// Project developers.  See the top-level LICENSE file for dates and other
// details.  No copyright assignment is required to contribute to VisIt.

// ************************************************************************* //
//                             avtTensorPlot.C                               //
// ************************************************************************* //

#include <avtTensorPlot.h>

#include <vtkSphereSource.h>

#include <avtCallback.h>
#include <avtGhostZoneFilter.h>
#include <avtLookupTable.h>
#include <avtResampleFilter.h>
#include <avtVariableLegend.h>
#include <avtTensorFilter.h>
#include <avtTensorGlyphMapper.h>

#include <InvalidLimitsException.h>

#include <string>

// ****************************************************************************
//  Method: avtTensorPlot constructor
//
//  Programmer: childs -- generated by xml2info
//  Creation:   Tue Sep 23 20:57:03 PST 2003
//
//  Modifications:
//    Kathleen Biagas, Thus Feb 7 11:19:41 PST 2013
//    Set sphere source's output port as input to the glyph mapper, preserves
//    pipeline connectivity with vtk-6.
//
// ****************************************************************************

avtTensorPlot::avtTensorPlot()
{
    colorsInitialized = false;
    TensorFilter = new avtTensorFilter(true, 10);
    resampleFilter = NULL;
    ghostFilter  = new avtGhostZoneFilter();
    ghostFilter->GhostDataMustBeRemoved();
    avtLUT       = new avtLookupTable();

    //
    // The tensor glyph mapper does funny things with normals.  Its best
    // just to remove the normals from the sphere source altogether.
    //
    vtkSphereSource *sphere = vtkSphereSource::New();
    tensorMapper  = new avtTensorGlyphMapper(sphere->GetOutputPort());
    // bump up the reference count
    sphere->Register(NULL);
    sphere->Delete();

    varLegend = new avtVariableLegend;
    varLegend->SetTitle("Tensor");

    //
    // This is to allow the legend to be reference counted so the behavior can
    // still access it after the plot is deleted.  The legend cannot be
    // reference counted all of the time since we need to know that it is a
    // VariableLegend.
    //
    varLegendRefPtr = varLegend;
}

// ****************************************************************************
//  Method: avtTensorPlot destructor
//
//  Programmer: childs -- generated by xml2info
//  Creation:   Tue Sep 23 20:57:03 PST 2003
//
// ****************************************************************************

avtTensorPlot::~avtTensorPlot()
{
    if (tensorMapper != NULL)
    {
        delete tensorMapper;
        tensorMapper = NULL;
    }
    if (TensorFilter != NULL)
    {
        delete TensorFilter;
        TensorFilter = NULL;
    }
    if (resampleFilter != NULL)
    {
        delete resampleFilter;
        resampleFilter = NULL;
    }
    if (ghostFilter != NULL)
    {
        delete ghostFilter;
        ghostFilter = NULL;
    }
    if (avtLUT != NULL)
    {
        delete avtLUT;
        avtLUT = NULL;
    }
}

// ****************************************************************************
//  Method:  avtTensorPlot::Create
//
//  Purpose:
//    Call the constructor.
//
//  Programmer: childs -- generated by xml2info
//  Creation:   Tue Sep 23 20:57:03 PST 2003
//
// ****************************************************************************

avtPlot *
avtTensorPlot::Create()
{
    return new avtTensorPlot;
}

// ****************************************************************************
//  Method:  avtTensorPlot::SetCellCountMultiplierForSRThreshold
//
//  Purpose: Sets the number of polygons each point in the plot's output will
//  be glyphed into.
//
//  Programmer:  Mark C. Miller 
//  Creation:    August 11, 2004 
//
//  Modifications:
//    Jeremy Meredith, Thu Aug 12 14:15:55 PDT 2004
//    Changed some code to get it to compile.
//
//    Mark C. Miller, Mon Aug 23 20:24:31 PDT 2004
//    Changed to Set... (Get is now done in avtPlot.C)
//
// ****************************************************************************

void
avtTensorPlot::SetCellCountMultiplierForSRThreshold(const avtDataObject_p dob)
{
    cellCountMultiplierForSRThreshold = 96.0;
}

// ****************************************************************************
//  Method: avtTensorPlot::GetMapper
//
//  Purpose:
//      Gets a mapper for this plot, it is actually a variable mapper.
//
//  Returns:    The variable mapper typed as its base class mapper.
//
//  Programmer: childs -- generated by xml2info
//  Creation:   Tue Sep 23 20:57:03 PST 2003
//
// ****************************************************************************

avtMapperBase *
avtTensorPlot::GetMapper(void)
{
    return tensorMapper;
}

// ****************************************************************************
//  Method: avtTensorPlot::ApplyOperators
//
//  Purpose:
//      Applies the operators associated with a Tensor plot.  
//      The output from this method is a query-able object.
//
//  Arguments:
//      input   The input data object.
//
//  Returns:    The data object after the Tensor plot has been applied.
//
//  Programmer: childs -- generated by xml2info
//  Creation:   Tue Sep 23 20:57:03 PST 2003
//
//  Modifications:
//
//    Hank Childs, Tue Feb 22 13:50:57 PST 2011
//    Filters used in this routine are actually part of the rendering
//    transformation.  Move them to the ApplyRenderingTransformation method.
//
// ****************************************************************************

avtDataObject_p
avtTensorPlot::ApplyOperators(avtDataObject_p input)
{
    return input;
}

// ****************************************************************************
//  Method: avtTensorPlot::ApplyRenderingTransformation
//
//  Purpose:
//      Applies the rendering transformation associated with a Tensor plot.  
//
//  Arguments:
//      input   The input data object.
//
//  Returns:    The data object after the Tensor plot has been applied.
//
//  Programmer: childs -- generated by xml2info
//  Creation:   Tue Sep 23 20:57:03 PST 2003
//
//  Modifications:
//
//    Hank Childs, Tue Feb 22 13:50:57 PST 2011
//    Filters being used in ApplyOperators were doing rendering transformations.
//    Put them here.
//
// ****************************************************************************

avtDataObject_p
avtTensorPlot::ApplyRenderingTransformation(avtDataObject_p input)
{
    ghostFilter->SetInput(input);

    avtDataObject_p dob = ghostFilter->GetOutput();

    if (atts.GetGlyphLocation() == TensorAttributes::UniformInSpace)
    {
        avtDataAttributes &atts = dob->GetInfo().GetAttributes();
        if (atts.GetTopologicalDimension() < atts.GetSpatialDimension())
        {
            avtCallback::IssueWarning("The option to place tensor glyphs "
              "uniformly in space only works when the spatial dimension "
              "matches the topological dimension.  The glyph locations will "
              "instead be a function of mesh resolution.");
        }
        else
        {
            resampleFilter->SetInput(dob);
            dob = resampleFilter->GetOutput();
        }
    }
    
    TensorFilter->SetInput(dob);

    return TensorFilter->GetOutput();
}

// ****************************************************************************
//  Method: avtTensorPlot::CustomizeBehavior
//
//  Purpose:
//      Customizes the behavior as appropriate for a Tensor plot.  This includes
//      behavior like shifting towards or away from the screen.
//
//  Programmer: childs -- generated by xml2info
//  Creation:   Tue Sep 23 20:57:03 PST 2003
//
// ****************************************************************************

void
avtTensorPlot::CustomizeBehavior(void)
{
    SetLimitsMode(atts.GetLimitsMode());
    behavior->SetShiftFactor(0.6);
    behavior->SetLegend(varLegendRefPtr);
    behavior->SetAntialiasedRenderOrder(ABSOLUTELY_LAST);
}

// ****************************************************************************
//  Method: avtTensorPlot::CustomizeMapper
//
//  Purpose:
//      A hook from the base class that allows the plot to change its mapper
//      based on the dataset input. 
//
//  Arguments:
//      doi     The data object information.
//
//  Programmer: childs -- generated by xml2info
//  Creation:   Tue Sep 23 20:57:03 PST 2003
//
//  Modifications:
//    Kathleen Bonnell, Mon Sep 29 12:31:18 PDT 2003
//    Set antialiased render order.
// ****************************************************************************

void
avtTensorPlot::CustomizeMapper(avtDataObjectInformation &doi)
{
    SetMapperColors();

    behavior->SetRenderOrder(DOES_NOT_MATTER);
    behavior->SetAntialiasedRenderOrder(DOES_NOT_MATTER);

    //
    // Set the legend ranges.
    //
    SetLegendRanges();
}

// ****************************************************************************
//  Method: avtTensorPlot::SetAtts
//
//  Purpose:
//      Sets the atts for the Tensor plot.
//
//  Arguments:
//      atts    The attributes for this Tensor plot.
//
//  Programmer: childs -- generated by xml2info
//  Creation:   Tue Sep 23 20:57:03 PST 2003
//
//  Modifications:
//    Eric Brugger, Wed Nov 24 13:17:56 PST 2004
//    I added scaleByMagnitude and autoScale.
//
//    Kathleen Bonnell, Mon Jan 17 18:20:03 MST 2011
//    Consider invertColorTable flag when setting updateColors.
//
// ****************************************************************************

void
avtTensorPlot::SetAtts(const AttributeGroup *a)
{
    const TensorAttributes *newAtts = (const TensorAttributes *)a;

    // See if the colors will need to be updated.
    bool updateColors = (!colorsInitialized) ||
       (atts.GetColorTableName() != newAtts->GetColorTableName()) ||
       (atts.GetInvertColorTable() != newAtts->GetInvertColorTable() ||
       (atts.GetColorByEigenValues() != newAtts->GetColorByEigenValues()));

    // See if any attributes that require the plot to be regenerated were
    // changed and copy the state object.
    needsRecalculation = atts.ChangesRequireRecalculation(*newAtts);
    atts = *newAtts;

    // Set internal plot state using the information in the atts state object.
    bool useStride = atts.GetUseStride();
    if (useStride)
    {
        TensorFilter->SetStride(atts.GetStride());
    }
    else
    {
        TensorFilter->SetNTensors(atts.GetNTensors());
    }
    TensorFilter->SetLimitToOriginal(atts.GetOrigOnly());

    // If the resample filter is not NULL, then we are calling SetAtts
    // for a second consecutive time.  The second call must be for SR
    // mode.  If we ever allow for pipeline re-execution, this code
    // will need to be changed and the resample filter will need to
    // have a new method added that allows for its atts to be set.
    // (They can only be set in the constructor right now.)
    if (resampleFilter == NULL)
    {
        InternalResampleAttributes resatts;
        resatts.SetUseTargetVal(true);
        resatts.SetDefaultVal(0.);
        resatts.SetDistributedResample(true);
        resatts.SetTargetVal(atts.GetNTensors());
        resampleFilter = new avtResampleFilter(&resatts);
    }

    tensorMapper->SetScaleByMagnitude(atts.GetScaleByMagnitude());
    tensorMapper->SetAutoScale(atts.GetAutoScale());
    tensorMapper->SetScale(atts.GetScale() * atts.GetAnimationScale());

    SetMapperColors();

    // Update the plot's colors if needed.
    if (atts.GetColorByEigenValues() &&
       (updateColors || atts.GetColorTableName() == "Default"))
    {
        colorsInitialized = true;
        SetColorTable(atts.GetColorTableName().c_str());
    }

    SetLimitsMode(atts.GetLimitsMode());

    //
    // Update the legend.
    //
    SetLegend(atts.GetUseLegend());
}

// ****************************************************************************
//  Method: avtTensorPlot::SetColorTable
//
//  Purpose:
//      Sets the plot's color table if the color table is the same as that of
//      the plot or we are using the default color table for the plot.
//
//  Arguments:
//      ctName : The name of the color table to use.
//
//  Returns:    Returns true if the color table is updated.
//
//  Programmer: Hank Childs
//  Creation:   September 23, 2003
//
//  Modifications:
//    Kathleen Bonnell, Mon Jan 17 18:20:03 MST 2011
//    Retrieve invertColorTable flag and pass to avtLUT.
//
// ****************************************************************************

bool
avtTensorPlot::SetColorTable(const char *ctName)
{
    bool retval = false;
    if (atts.GetColorByEigenValues())
    {
        bool namesMatch = (atts.GetColorTableName() == std::string(ctName));
        bool invert = atts.GetInvertColorTable();
        if (atts.GetColorTableName() == "Default")
        {
            retval =  avtLUT->SetColorTable(NULL, namesMatch, false, invert);
        }
        else
        {
            retval =  avtLUT->SetColorTable(ctName, namesMatch, false, invert);
        }

        if (retval)
        {
            tensorMapper->SetLookupTable(avtLUT->GetLookupTable());
        }
    }
    else
    {
        avtLUT->SetLUTColors(atts.GetTensorColor().GetColor(), 1);
    }

    return retval;
}

// ****************************************************************************
//  Method: avtTensorPlot::SetLegend
//
//  Purpose:
//    Turns the legend on or off.
//
//  Arguments:
//    legendOn  : true if the legend should be turned on, false otherwise.
//
//  Programmer: Hank Childs
//  Creation:   September 23, 2003
//
// ****************************************************************************

void
avtTensorPlot::SetLegend(bool legendOn)
{
    if (legendOn)
    {
        // Set scaling.
        varLegend->LegendOn();
        varLegend->SetLookupTable(avtLUT->GetLookupTable());
        varLegend->SetScaling();
    }
    else
    {
        varLegend->LegendOff();
    }
}

// ****************************************************************************
//  Method: avtTensorPlot::SetLegendRanges
//
//  Purpose:
//      Sets the range to use for the legend.
//
//  Programmer: Hank Childs
//  Date:       September 23, 2003
//
//  Modifications:
//    Eric Brugger, Wed Mar  8 16:50:08 PST 2023
//    Modified the range limits text to use the current variable limits
//    if OriginalData is not set.
//
// ****************************************************************************

void
avtTensorPlot::SetLegendRanges()
{
    double min = 0., max = 1.;

    if (atts.GetLimitsMode() == TensorAttributes::OriginalData)
    {
        tensorMapper->GetRange(min, max);
    }
    else
    {
        tensorMapper->GetCurrentRange(min, max);
    }
    varLegend->SetRange(min, max);

    //
    // Set the range for the legend's text and colors.
    //
    if (atts.GetLimitsMode() == TensorAttributes::OriginalData)
        tensorMapper->GetVarRange(min, max);
    else
        tensorMapper->GetCurrentVarRange(min, max);
    varLegend->SetVarRange(min, max);
}

// ****************************************************************************
//  Method: avtTensorPlot::ReleaseData
//
//  Purpose:
//      Release the problem sized data associated with this plot.
//
//  Programmer: Hank Childs
//  Creation:   September 12, 2002
//
// ****************************************************************************

void
avtTensorPlot::ReleaseData(void)
{
    avtPointDataPlot::ReleaseData();

    if (TensorFilter != NULL)
    {
        TensorFilter->ReleaseData();
    }
    if (resampleFilter != NULL)
    {
        resampleFilter->ReleaseData();
    }
    if (ghostFilter != NULL)
    {
        ghostFilter->ReleaseData();
    }
}

// ****************************************************************************
//  Method: avtTensorPlot::SetMapperColors
//
//  Purpose:
//    Tells the tensorMapper how to color the data. 
//
//  Programmer: Kathleen Bonnell 
//  Creation:   August 12, 2004 
//
// ****************************************************************************

void
avtTensorPlot::SetMapperColors()
{
    if (atts.GetColorByEigenValues())
    {
        tensorMapper->ColorByMagOn();
    }
    else
    {
        const unsigned char *col = atts.GetTensorColor().GetColor();
        avtLUT->SetLUTColors(col, 1);
        tensorMapper->ColorByMagOff(col);
    }
}

// ****************************************************************************
//  Method: avtTensorPlot::SetLimitsMode
//
//  Purpose:  To determine the proper limits the mapper should be using.
//
//  Arguments:
//    limitsMode  Specifies which type of limits.
//
//  Programmer:   Kathleen Bonnell
//  Creation:     December 22, 2004 
//
//  Modifications:
//
// ****************************************************************************

void
avtTensorPlot::SetLimitsMode(int limitsMode)
{
    double min, max;
    //
    //  Retrieve the actual range of the data
    //
    tensorMapper->GetVarRange(min, max);

    float userMin = atts.GetMinFlag() ? atts.GetMin() : min;
    float userMax = atts.GetMaxFlag() ? atts.GetMax() : max;
      
    if (dataExtents.size() == 2)
    {
        tensorMapper->SetMin(dataExtents[0]);
        tensorMapper->SetMax(dataExtents[1]);
    }
    else if (atts.GetMinFlag() && atts.GetMaxFlag())
    {
        if (userMin >= userMax)
        {
            EXCEPTION1(InvalidLimitsException, false); 
        }
        else
        {
            tensorMapper->SetMin(userMin);
            tensorMapper->SetMax(userMax);
        }
    } 
    else if (atts.GetMinFlag())
    {
        tensorMapper->SetMin(userMin);
        if (userMin > userMax)
        {
            tensorMapper->SetMax(userMin);
        }
        else
        {
            tensorMapper->SetMaxOff();
        }
    }
    else if (atts.GetMaxFlag())
    {
        tensorMapper->SetMax(userMax);
        if (userMin > userMax)
        {
            tensorMapper->SetMin(userMax);
        }
        else
        {
            tensorMapper->SetMinOff();
        }
    }
    else
    {
        tensorMapper->SetMinOff();
        tensorMapper->SetMaxOff();
    }

    tensorMapper->SetLimitsMode(limitsMode);

    SetLegendRanges();
}

// ****************************************************************************
//  Method: avtTensorPlot::GetExtraInfoForPick
//
//  Purpose:
//    Override default settings for extraPickinfo.
//
//  Programmer: Kathleen Biagas
//  Creation:   February 29, 2012
//
// ****************************************************************************

const MapNode &
avtTensorPlot::GetExtraInfoForPick()
{
    extraPickInfo["glyphPickAlways"] = true;
    extraPickInfo["nodeCenteredNeedZonesForPick"] = false;

    return extraPickInfo;
}
